package bank.model.pool;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

import bank.model.properties.DBConnectionProperties;


public class DBConnectionPool implements DBConnectionProperties {
	private static int clients = 0;
	private PrintWriter log;
	private static DBConnectionPool instance;
	private ArrayList<Connection> freeConnections = new ArrayList<Connection>();
	
	private DBConnectionPool()
	{
		  String logFile = "DBConnection.log";
		  try {
		   log = new PrintWriter(new FileWriter(logFile, true), true);
		  }
		  catch (IOException e) {
		    System.err.println("[" + new Date() + "] Can't open the log file: " + logFile + "\n");
		   log = new PrintWriter(System.err);
		  }
		  loadDrivers();
	}
	
	private void loadDrivers()
	{
		try 
		{
			Driver driver = (Driver)Class.forName(DB_DRIVER).newInstance();
		    DriverManager.registerDriver(driver);
		    log.write("[" + new Date() + "] Registered JDBC driver \n");
		}
		catch (Exception e)
		{
		    log.write("[" + new Date() + "] Can't register JDBC driver \n");
		}
	}
	
	static synchronized public DBConnectionPool	getInstance() 
	{
		if (instance == null) {
			instance = new DBConnectionPool();
		}
		clients++;
		return instance;
	}
	
	public synchronized Connection getConnection()
	{
		Connection con = null;
		if (!freeConnections.isEmpty())
		{
			con = (Connection) freeConnections.get(freeConnections.size()-1);
			log.write("[" + new Date() + "] Give a connection in pool \n");
		    freeConnections.remove(con);
		    try 
		    {
		    	if (con.isClosed())
		    	{
		    		log.write("[" + new Date() + "] Removed bad connection \n");
		    		// Try again recursively
		    		con = getConnection();
		    	}
		    }
		    catch (SQLException e)
		    {
		    	log.write("[" + new Date() + "] Removed bad connection \n");
		    	// Try again recursively
		    	con = getConnection();
		    }
		    catch (Exception e)
		    {
		    	log.write("[" + new Date() + "] Removed bad connection \n");
		    	// Try again recursively
		    	con = getConnection();
		    }
		    return con;
		}
		else if (DB_MAX_CONNECTIONS >= clients)
		{
			con = newConnection();
			return con;
		}
		else
		{
			log.write("[" + new Date() + "] Too many connections... \n");
			return null;
		}
		
	}
	
	private Connection newConnection()
	{
		Connection con = null;
		try
		{
			if (DB_USER == null)
			{
				con = DriverManager.getConnection(DB_URL);
				clients++;
		    }
		    else
		    {
		    	con = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
		    	clients++;
		    }
		    log.write("[" + new Date() + "] Created a new connection in pool \n");
		}
		catch (SQLException e)
		{
			log.write("[" + new Date() + "] Can't create a new connection for " + DB_URL + "\n");
		    return null;
		}
		return con;
	}
	
	public synchronized void freeConnection(Connection con)
	{
		// Put the connection at the end of the List
		if ( (con != null) && (freeConnections.size() <= DB_MAX_CONNECTIONS) )
		{
			freeConnections.add(con);
			clients--;
		}
	}
	
	public synchronized void release()
	{
		Iterator allConnections = freeConnections.iterator();
		while (allConnections.hasNext())
		{
			Connection con = (Connection) allConnections.next();
		    try
		    {
		    	con.close();
		    	log.write("[" + new Date() + "] Closed connection for pool \n");
		    }
		    catch (SQLException e)
		    {
		    	log.write("[" + new Date() + "] Can't close connection for pool \n");
		    }
		}
		freeConnections.clear();
		log.close();
	}
}
